/* @flow */

import { ASSET_TYPES } from 'shared/constants';
import { defineComputed, proxy } from '../instance/state';
import { extend, mergeOptions, validateComponentName } from '../util/index';

export function initExtend(Vue: GlobalAPI) {
    /**
     * Each instance constructor, including Vue, has a unique
     * cid. This enables us to create wrapped "child
     * constructors" for prototypal inheritance and cache them.
     */
    Vue.cid = 0;
    let cid = 1;

    /**
     * Class inheritance
     * 组件的options，组件的选项是一个对象
     */
    Vue.extend = function (extendOptions: Object): Function {
        extendOptions = extendOptions || {};
        // Vue 构造函数
        const Super = this;
        const SuperId = Super.cid;
        // 从缓存中加载组件的构造函数
        const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {});
        if (cachedCtors[SuperId]) {
            return cachedCtors[SuperId];
        }

        const name = extendOptions.name || Super.options.name;
        if (process.env.NODE_ENV !== 'production' && name) {
            // 如果是开发环境验证组件的名称
            validateComponentName(name);
        }
        // 组件的构造函数
        const Sub = function VueComponent(options) {
            // 调用 _init() 初始化
            this._init(options);
        };
        // 原型继承自 Vue。所有的组件继承Vue，继承Vue的选项options
        Sub.prototype = Object.create(Super.prototype);
        Sub.prototype.constructor = Sub;
        Sub.cid = cid++;
        // 合并 options
        Sub.options = mergeOptions(Super.options, extendOptions);
        Sub['super'] = Super;

        // For props and computed properties, we define the proxy getters on
        // the Vue instances at extension time, on the extended prototype. This
        // avoids Object.defineProperty calls for each instance created.
        if (Sub.options.props) {
            initProps(Sub);
        }
        if (Sub.options.computed) {
            initComputed(Sub);
        }

        // allow further extension/mixin/plugin usage
        // 将Super里的函数拷贝到Sub（就是组件的构造函数）中
        Sub.extend = Super.extend;
        Sub.mixin = Super.mixin;
        Sub.use = Super.use;

        // create asset registers, so extended classes
        // can have their private assets too.
        // 注册vue组件的component、filter、directive等方法
        ASSET_TYPES.forEach(function (type) {
            Sub[type] = Super[type];
        });
        // enable recursive self-lookup
        if (name) {
            Sub.options.components[name] = Sub;
        }

        // keep a reference to the super options at extension time.
        // later at instantiation we can check if Super's options have
        // been updated.
        Sub.superOptions = Super.options;
        Sub.extendOptions = extendOptions;
        Sub.sealedOptions = extend({}, Sub.options);

        // cache constructor
        // 把组件的构造函数缓存到 options._Ctor
        cachedCtors[SuperId] = Sub;
        return Sub;
    };
}

function initProps(Comp) {
    const props = Comp.options.props;
    for (const key in props) {
        proxy(Comp.prototype, `_props`, key);
    }
}

function initComputed(Comp) {
    const computed = Comp.options.computed;
    for (const key in computed) {
        defineComputed(Comp.prototype, key, computed[key]);
    }
}
